package top.maof.mfcoin.block;

import lombok.Data;
import lombok.NoArgsConstructor;
import top.maof.mfcoin.algo.SecretUtil;
import top.maof.mfcoin.tx.Transaction;

import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * 区块body
 * 存放merkleTree
 * 为了处理方便，merkleTree的存放采用hashmap
 *
 */
@Data
@NoArgsConstructor
public class BlockBody {
    /**
     * merkleTree
     * <p>
     * key: tx的hash
     * value：未放入区块的Transaction
     */
    private Map<String, Transaction> merkleTree;

    // merkleTree根hash值，base64编码
    private String hashMerkleTree;


    public BlockBody(Map<String, Transaction> merkleTree) {
        this.merkleTree = merkleTree;
        this.hashMerkleTree = hashMerkleTree(this.merkleTree);
    }

    /**
     * 求MerkleTree的根hash值
     * 不验证交易的合法性
     */
    public static String hashMerkleTree(Map<String, Transaction> merkleTree) {

        Set<Map.Entry<String, Transaction>> entries = merkleTree.entrySet();
        List<byte[]> list = new ArrayList(entries.size());

        for (Map.Entry<String, Transaction> entry : entries) {
            list.add(Transaction.hash(entry.getValue()));
        }
        return Base64.getEncoder().encodeToString(doHashMerkleTree(list));
    }

    /**
     * hashMerkleTree()方法的实际执行方法
     */
    private static byte[] doHashMerkleTree(List<byte[]> list) {
        if (list.size() == 1) {
            return list.get(0);
        }
        List<byte[]> newList = new ArrayList<>(list.size() / 2 + 1);

        for (int i = 0; i < list.size(); i += 2) {
            if (i + 1 < list.size()) {
                byte[] bt3 = new byte[list.get(i).length + list.get(i + 1).length];
                System.arraycopy(list.get(i), 0, bt3, 0, list.get(i).length);
                System.arraycopy(list.get(i + 1), 0, bt3, list.get(i).length, list.get(i + 1).length);
                try {
                    newList.add(SecretUtil.digest(SecretUtil.DIGEST_ALGORITHM_SHA256, bt3));
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
            } else {
                try {
                    newList.add(SecretUtil.digest(SecretUtil.DIGEST_ALGORITHM_SHA256, list.get(i)));
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
            }
        }
        return doHashMerkleTree(newList);
    }

}
